/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_PRISM_H
#define _GCTL_PRISM_H

#include "vertex.h"
#include "entity.h"

namespace gctl
{
	// Declaration of the basic prism type
	template <typename A> struct type_prism;

	typedef type_prism<void> prism; // prism type of attribute type of void

	/**
	 * 
	 * @brief      structure of a regular prism under the Cartesian coordinates.
	 * 
	 * @tparam     A     Attribute type
	 * 
	 * Coordinate system used in the algorithm.
	 * 
	 * z   y
	 * |  /      3
	 * | /      /|\
	 * |/      / | \
	 * |      /  |  \
	 * |     /  / \0 \
	 * |   4|---------| 5
	 * |    | /     \ | 
	 * |    |/-------\|
	 * |   1          2 
	 * O-----------------> x
	 * 
	 */
	template <typename A>
	struct type_prism : public entity<vertex3dc, 6, A>
	{
		/**
		 * neighbors of the prism stored in the order x neighbor on left, x neighbor on right, 
		 * y neighbor in front, y neighbor in back, z neighbor in bottom and z neighbor in top.
		 */
		type_prism<A> *neigh[5];
		/**
		 * constructor
		 */
		type_prism();
		/**
		 * @brief      Constructs an object from a vertex array
		 *
		 * @param[in]  vert_arr  The vertex array
		 * @param[in]  n0_id     The vertex 0 index
		 * @param[in]  n1_id     The vertex 1 index
		 * @param[in]  n2_id     The vertex 2 index
		 * @param[in]  n3_id     The vertex 3 index
		 * @param[in]  n4_id     The vertex 4 index
		 * @param[in]  n5_id     The vertex 5 index
		 * @param[in]  index     The element index
		 */
		type_prism(const array<vertex3dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, 
			int n4_id, int n5_id, int index = 0);
		/**
		 * @brief      de-constructor
		 */
		virtual ~type_prism(){}
		/**
		 * @brief      Constructs an object from a vertex array
		 *
		 * @param[in]  vert_arr  The vertex array
		 * @param[in]  n0_id     The vertex 0 index
		 * @param[in]  n1_id     The vertex 1 index
		 * @param[in]  n2_id     The vertex 2 index
		 * @param[in]  n3_id     The vertex 3 index
		 * @param[in]  n4_id     The vertex 4 index
		 * @param[in]  n5_id     The vertex 5 index
		 * @param[in]  index     The element index
		 */
		void set(const array<vertex3dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, 
			int n4_id, int n5_id, int index = 0);
		/**
         * @brief      Reset the structure to initial state
         */
        void reset();
	};

	template <typename A>
	type_prism<A>::type_prism() : entity<vertex3dc, 6, A>::entity()
	{
		for (int i = 0; i < 5; ++i)
		{
			neigh[i] = nullptr;
		}
	}

	template <typename A>
	type_prism<A>::type_prism(const array<vertex3dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, 
		int n4_id, int n5_id, int index) : type_prism()
	{
		set(vert_arr, n0_id, n1_id, n2_id, n3_id, n4_id, n5_id, index);
	}

	template <typename A>
	void type_prism<A>::set(const array<vertex3dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, 
		int n4_id, int n5_id, int index)
	{
		if (index < 0)
	    {
	        throw out_of_range("Invalid index number, From type_prism::set(...)");
	    }

	    if (vert_arr.size() < 6)
	    {
	        throw length_error("Invalid array size, From type_prism::set(...)");
	    }

		this->id = index;
		this->vert[0] = vert_arr.get(n0_id);
		this->vert[1] = vert_arr.get(n1_id);
		this->vert[2] = vert_arr.get(n2_id);
		this->vert[3] = vert_arr.get(n3_id);
		this->vert[4] = vert_arr.get(n4_id);
		this->vert[5] = vert_arr.get(n5_id);
		return;
	}

	template <typename A>
	void type_prism<A>::reset()
	{
	    entity<vertex3dc, 6, A>::reset();
	    for (int i = 0; i < 5; ++i)
		{
			neigh[i] = nullptr;
		}
	    return;
	}

	template <typename A>
	void copy_type_prism(type_prism<A> *tar, const type_prism<A> *src)
	{
		copy_entity(tar, src);

		for (size_t i = 0; i < 5; i++)
		{
			tar->neigh[i] = src->neigh[i];
		}
		return;
	}

	template <typename A, typename B>
	void copy_type_prism(type_prism<A> *tar, const type_prism<B> *src)
	{
		copy_entity(tar, src);
		return;
	}
}

#endif // _GCTL_PRISM_H